﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;
using UnityEngine;

namespace Assets
{
    internal static class ClientNetwork
    {
        static TcpClient client;
        private static bool connectingFlag;

        public static bool IsConnected { get; private set; }

        static System.Object lockObj = new System.Object();
        /// <summary>
        /// 连接服务器
        /// </summary>
        public static void ConnectServer(string host, int port)
        {
            Debug.LogFormat("start connect server host:{0}, port:{1}", host, port);
            lock (lockObj)
            {
                // 关闭老的连接
                if (null != client)
                {
                    //Close();
                }
                // 建立新的连接
                client = new TcpClient();
                client.SendTimeout = 1000;
                client.ReceiveTimeout = 1000;
                client.NoDelay = true;
                IsConnected = false;
                connectingFlag = true;
                try
                {
                    client.BeginConnect(host, port, new AsyncCallback(OnConnect), client);

                    // 这里是一个任务管理器，可以用来执行定时任务。连接时候添加一个超时检查的定时任务。
                    //TimerManager timer = AppFacade.Instance.GetManager<TimerManager>(ManagerName.Timer);
                    //timer.AddTask(OnConnectTimeout, CONN_TIMEOUT);
                }
                catch (Exception e)
                {
                    Debug.LogErrorFormat("connect server error", e);
                    // 通知连接失败
                    //NetworkManager.AddEvent(Protocal.ConnectFail, null);
                }
            }
        }
        const short MAX_READ = 4096;
        static byte[] byteBuffer = new byte[MAX_READ];
        internal static Action<bool> actionOnConnectResult;
        internal static Action<ushort,ushort,byte[],int> actionOnRecv;
        private static void OnConnect(IAsyncResult ar)
        {
            lock (lockObj)
            {
                TcpClient clientAr = (TcpClient)ar.AsyncState;
                bool validConn = (client == clientAr);
                connectingFlag = false;
                try
                {
                    // 结束异步连接
                    client.EndConnect(ar);

                    // 非当前连接
                    if (!validConn)
                    {
                        client.Close();
                    }

                    if (client.Connected)
                    {
                        Debug.Log("connect server succ");

                        // 异步读socket数据
                        var socketStream = client.GetStream();
                        socketStream.BeginRead(byteBuffer, 0, MAX_READ, new AsyncCallback(OnRead), socketStream);

                        // 通知连接成功
                        IsConnected = true;
                        actionOnConnectResult(true);//NetworkManager.AddEvent(Protocal.Connect, null);
                    }
                    else
                    {
                        // 通知连接失败
                        Debug.LogError("connect server failed");

                        actionOnConnectResult(false);//NetworkManager.AddEvent(Protocal.ConnectFail, null);
                    }
                }
                catch (SocketException e)
                {
                    Debug.LogErrorFormat("connect error", e);

                    if (validConn)
                    {
                        // 通知连接失败
                        //NetworkManager.AddEvent(Protocal.ConnectFail, null);
                    }
                    else
                    {
                        client.Close();
                    }

                    actionOnConnectResult(false);
                }
            }
        }
        /// <summary>
        /// 读取消息
        /// </summary>
        static void OnRead(IAsyncResult asr)
        {
            //int bytesRead = 0; // 读取到的字节
            bool validConn = false; // 是否是合法的连接

            //SocketState socketState = (SocketState)asr.AsyncState;
            //TcpClient client = socketState.client;
            if (client == null || !client.Connected)
            {
                return;
            }
            NetworkStream socketStream = (NetworkStream)asr.AsyncState;//socketState.socketStream;

            lock (lockObj)
            {
                try
                {
                    validConn = true; //(client == this.client);

                    // 读取字节流到缓冲区
                    var bytesRead = socketStream.EndRead(asr);

                    if (bytesRead < 1)
                    {
                        if (!validConn)
                        {
                            // 已经重新连接过了
                            socketStream.Close();
                            client.Close();
                        }
                        else
                        {
                            // 被动断开时
                            // 通知连接被断开
                            //OnDisconnected(DisType.Disconnect, "bytesRead < 1");
                        }
                        return;
                    }

                    // 接受数据包，写入缓冲区
                    onRecv(byteBuffer, bytesRead);//OnReceive(byteBuffer, bytesRead);

                    // 再次监听服务器发过来的新消息
                    Array.Clear(byteBuffer, 0, byteBuffer.Length);   //清空数组
                    socketStream.BeginRead(byteBuffer, 0, MAX_READ, new AsyncCallback(OnRead), socketStream);
                    // 对消息进行解码
                    if (bytesRead > 0)
                    {
                        //OnDecodeMessage();
                    }
                }
                catch (Exception e)
                {
                    Debug.LogErrorFormat("read data error, connect valid:{0}{1}{2}",
                        e.Message,e.StackTrace ,validConn);

                    if (validConn)
                    {
                        // 通知连接被断开
                        //OnDisconnected(DisType.Exception, e);
                    }
                    else
                    {
                        socketStream.Close();
                        client.Close();
                    }
                }
            }


        }
        static System.IO.MemoryStream msRecv_ =new System.IO.MemoryStream();
        static void onRecv(byte[] buf, int size)
        {
            msRecv_.Write(buf,0,size);
            while(Get1Package())
            {

            }
        }
        static bool Get1Package()
        {
            if( 6 > msRecv_.Length )
                return false;

            var buf = msRecv_.GetBuffer();
            var oldLenght = msRecv_.Length;
            var size = System.BitConverter.ToUInt16(buf,0);
            var main = System.BitConverter.ToUInt16(buf,2);
            var sub = System.BitConverter.ToUInt16(buf,4);
            if( size > msRecv_.Length )
                return false;

            var byteArr1Package = new byte[size];

            actionOnRecv(main,sub,buf,size);
            msRecv_ = new System.IO.MemoryStream();
            msRecv_.Write(buf,size,(int)oldLenght-size);
            return true;
        }
        internal static NetworkStream GetSendStream()
        {
            var s = client.GetStream();
            return s;
        }
        static void sendUshort(ushort us)
        {
            var s = client.GetStream();
            var arrByte = System.BitConverter.GetBytes(us);
            s.Write(arrByte,0,arrByte.Length);
        }
        const ushort HEAD_SIZE=6;
        internal static void sendMsg(ushort main ,ushort sub, Google.Protobuf.IMessage msg)
        {
            var arrByte = Google.Protobuf.MessageExtensions.ToByteArray(msg);
            Assets.ClientNetwork.sendUshort((ushort)(arrByte.Length+HEAD_SIZE));//包头3个ushort
            Assets.ClientNetwork.sendUshort(main);
            Assets.ClientNetwork.sendUshort(sub);
            var s = client.GetStream();
            s.Write( arrByte ,0,(int)arrByte.Length);
            s.Flush();
        }
        
        internal static T parseMsg<T>(byte[] buf, int size)where T : Google.Protobuf.IMessage,new()
        {
            T message = new T();
            Google.Protobuf.MessageExtensions.MergeFrom(message,buf, HEAD_SIZE, size-HEAD_SIZE, false, null);//包头3个ushort
            return message;
        }
    }
}
